package org.theqcp.wow.api.wowarmory.parsers;

import java.util.HashMap;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
import org.theqcp.wow.api.wowarmory.WowArmory;
import org.theqcp.wow.api.wowhead.WowHead;
import org.theqcp.wow.model.api.ProgressListener;
import org.theqcp.wow.model.api.ProgressListenerAdapter;
import org.theqcp.wow.model.exceptions.EquipmentNotFoundException;
import org.theqcp.wow.model.exceptions.PlayerRaceNotAllowedException;
import org.theqcp.wow.model.exceptions.ServiceUnavailableException;
import org.theqcp.wow.model.items.Equipment;
import org.theqcp.wow.model.mechanics.Attribute;
import org.theqcp.wow.model.mechanics.ClassType;
import org.theqcp.wow.model.mechanics.Race;
import org.theqcp.wow.model.mechanics.Slot;
import org.theqcp.wow.model.player.Player;
import org.theqcp.wow.model.player.PlayerFactory;

/**
 * Parses player data from xml document from wowarmory
 * 
 * @author angryundead
 * 
 */
public class PlayerParser extends AbstractParser {

    public Player parse( Document inputDoc, final ProgressListener listener ) throws JDOMException {

	// create wowhead instance
	WowHead wh = new WowHead();

	// create wowarmory instance
	WowArmory wa = new WowArmory();

	// player
	Player p = null;

	// get element(s) with xpath
	XPath xpath = XPath.newInstance("/page/characterInfo");

	// list
	@SuppressWarnings( "unchecked" )
	List<Object> elementList = xpath.selectNodes(inputDoc);

	// remains
	double percent = 0;

	if( elementList != null && elementList.size() > 0 ) {

	    // update listener
	    if( listener != null ) {
		listener.updateProgress(percent, "Parsing player.");
	    }

	    // get first (character) node
	    Element characterElement = (Element) elementList.get(0);

	    // return null because blizz is too stupid to send a rational
	    // message
	    if( characterElement == null || characterElement.getChildren().size() == 0 )
		return null;

	    String errCode = characterElement.getAttributeValue("errCode");

	    // return null if error code is "noCharacter" when blizz decides to
	    // actually
	    // allow us to have an error code
	    if( "noCharacter".equalsIgnoreCase(errCode) )
		return null;

	    // get name, realm, class, race
	    String name = characterElement.getChild("character").getAttributeValue("name");
	    String clazz = characterElement.getChild("character").getAttributeValue("class");
	    String realm = characterElement.getChild("character").getAttributeValue("realm");
	    String race = characterElement.getChild("character").getAttributeValue("race");

	    // get enums
	    ClassType c = ClassType.getClassFromName(clazz);
	    Race r = Race.getRaceFromName(race);

	    // create object
	    if( name != null && !name.isEmpty() && realm != null && !realm.isEmpty() && r != null ) {

		try {
		    // get character tab in character node
		    Element characterTab = characterElement.getChild("characterTab");

		    // get data elements from character tab
		    String baseStrength = characterTab.getChild("baseStats").getChild("strength").getAttributeValue("base");
		    String baseAgility = characterTab.getChild("baseStats").getChild("agility").getAttributeValue("base");
		    String baseStamina = characterTab.getChild("baseStats").getChild("stamina").getAttributeValue("base");
		    String baseIntellect = characterTab.getChild("baseStats").getChild("intellect").getAttributeValue(
		    	"base");
		    String baseSpirit = characterTab.getChild("baseStats").getChild("spirit").getAttributeValue("base");

		    // power/melee
		    Element melee = characterTab.getChild("melee");
		    String basePower = melee.getChild("power").getAttributeValue("base");

		    // create player
		    p = PlayerFactory.createPlayer(c);

		    // set race, name realm
		    p.setRace(r);
		    p.setName(name);
		    p.setRealm(realm);

		    // set base stats
		    p.setBaseStat(Attribute.STRENGTH, this.convertSafeDouble(baseStrength));
		    p.setBaseStat(Attribute.AGILITY, this.convertSafeDouble(baseAgility));
		    p.setBaseStat(Attribute.STAMINIA, this.convertSafeDouble(baseStamina));
		    p.setBaseStat(Attribute.INTELLECT, this.convertSafeDouble(baseIntellect));
		    p.setBaseStat(Attribute.SPIRIT, this.convertSafeDouble(baseSpirit));
		    p.setBaseStat(Attribute.ATTACKPOWER, this.convertSafeDouble(basePower));

		    // attempt to get the profession node
		    Element professions = characterTab.getChild("professions");

		    // if there is a profession node
		    if( professions != null ) {
		        // tradeskills
		        for( Object o: professions.getChildren() ) {
		    	if( o instanceof Element ) {
		    	    // get skill element
		    	    Element skillElement = (Element) o;

		    	    // make sure is skill
		    	    if( skillElement.getName().equalsIgnoreCase("skill") ) {
		    		String key = skillElement.getAttributeValue("key");
		    		String value = skillElement.getAttributeValue("value");
		    		Integer vInt = this.convertSafeInt(value);

		    		// set tradeskill
		    		p.setTradeSkillValue(key, vInt);
		    	    }
		    	}
		        }
		    }

		    // equiped item listing
		    HashMap<String, Slot> itemMap = new HashMap<String, Slot>();

		    @SuppressWarnings( "unchecked" )
		    List<Element> itemElementList = characterTab.getChild("items").getChildren("item");
		    for( Element itEl: itemElementList ) {
		        itemMap.put(itEl.getAttributeValue("id"),
		    	    Slot.getSlotByIndex(Integer.valueOf(itEl.getAttributeValue("slot"))));
		    }

		    // update percentages
		    double remains = 100.0 / 2.0;
		    percent += remains;

		    // update listener
		    if( listener != null ) {
		        listener.updateProgress(percent, "Player parsing complete, parsing weapons.");
		    }

		    // get chunck size
		    remains = remains / (itemMap.size() * 1.0);

		    // equip items
		    for( String id: itemMap.keySet() ) {
		        // items come from wowhead
		        Equipment e = null;

		        // get slot
		        Slot s = itemMap.get(id);

		        // update listener
		        if( listener != null ) {
		    	listener.updateProgress(percent, "Getting [" + s + "] [" + id + "]");
		        }

		        // create adapter
		        ProgressListenerAdapter pla = new ProgressListenerAdapter(listener, percent, (percent + remains));

		        try {
		    	e = wh.getEquipmentByID(id, pla);
		        } catch( ServiceUnavailableException e1 ) {
		    	// fall back on wowarmory
		    	try {
		    	    e = wa.getEquipmentByID(id, pla);
		    	} catch( EquipmentNotFoundException e2 ) {
		    	    // what the crap do we do if
		    	    // the item isn't in wow armory
		    	    // or it isn't in wowhead
		    	} catch( ServiceUnavailableException e2 ) {
		    	    // if both services are down, we can't
		    	    // even get to this point.
		    	}
		        } catch( EquipmentNotFoundException e1 ) {
		    	// fall back on wowarmory
		    	try {
		    	    e = wa.getEquipmentByID(id, pla);
		    	} catch( EquipmentNotFoundException e2 ) {
		    	    // what the crap do we do if
		    	    // the item isn't in wow armory
		    	    // or it isn't in wowhead
		    	} catch( ServiceUnavailableException e2 ) {
		    	    // if both services are down, we can't
		    	    // even get to this point.
		    	}
		        }

		        // only do this if the equipment is
		        // not null so that only equipment
		        // that came from a service is entered
		        if( e != null ) {
		    	e.setEquipSlot(s);
		    	p.equipItem(s, e);
		        }

		        // update progress
		        percent += remains;
		        // update listener
		        if( listener != null ) {
		    	listener.updateProgress(percent);
		        }
		    }
		} catch( NumberFormatException e ) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		} catch( PlayerRaceNotAllowedException e ) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
	    }
	}

	return p;
    }

}
